package edu.ucla.wanda.social;

import java.io.*;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.gson.stream.JsonReader;

import edu.ucla.wanda.db.WandaSocialDbAdapter;
import edu.ucla.wanda.db.WandaSubjectDbAdapter;



import sun.misc.BASE64Decoder;

/**
 * Servlet implementation class FbAuthServlet
 */
public class FbAuthServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public FbAuthServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
    @Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doPost(request, response);
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String code = request.getParameter("code");
		String action = request.getParameter("action");
		String error = request.getParameter("error_reason");
		UserSession sessionBean = (UserSession)request.getSession().getAttribute("userSession");
		
		// need to make sure that we have a session bean, in case it hasn't been instantiated
		if(null == sessionBean)
		{
			sessionBean = new UserSession();
			request.getSession().setAttribute("userSession", sessionBean);
		}
		
		response.setContentType("text/html");
		response.setBufferSize(8192);
		
		
		PrintWriter out = response.getWriter();
		
		if(null != error)
		{
			out.println("<html><head><meta http-equiv=\"Refresh\" content=\"0; url=http://apps.facebook.com/wanda_social\"</head></html>");
		}
		
		
		else
		{
			// any time the app is loaded from the Facebook canvas, the signed_request
			// parameter will be passed
			String signed_request_string = request.getParameter("signed_request");
			SignedRequest req = new SignedRequest();
			
			// get request string
			if(null != signed_request_string)
			{
				String[] encoded_tokens;
				String decoded_payload;
				BASE64Decoder decoder = new BASE64Decoder();
				
				// string consists of a signature, a '.', and the payload
				encoded_tokens = signed_request_string.split("\\.");
				
				decoded_payload = new String(decoder.decodeBuffer(encoded_tokens[1]));
				
				JsonReader reader = new JsonReader(new StringReader(decoded_payload));
				
				reader.beginObject();
				
				// read the payload and extract fields we care about
				while(reader.hasNext()){
					String name = reader.nextName();
					
					// algorithm used to compute the signature
					if(name.equals("algorithm")){
						req.algorithm = reader.nextString();
					}

					// FB profile id
					else if(name.equals("user_id")){
						req.user_id = reader.nextString();
					}
					
					// token for oauth
					else if(name.equals("oauth_token")){
						req.oauth_token = reader.nextString();
					}
					
					// oauth expiration token
					else if(name.equals("expires")){
						req.token_expires = reader.nextString();
					}
					
					else{
						reader.skipValue();
					}
				}
				
				reader.endObject();
				
				// this should always be set
				if(!"HMAC-SHA256".equals(req.algorithm))
				{
					out.println("Unknown algorithm. Expected HMAC-SHA256");
					out.close();
					return;
				}
					 
				// need to compare the hashes
				Mac sha256_HMAC;
				
				try {
					sha256_HMAC = Mac.getInstance("HmacSHA256");
				} catch (NoSuchAlgorithmException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					
					out.println(e);
					out.close();
					return;	
				}
				
				try {
					// initialize the hash using the FB app secret key
					sha256_HMAC.init(new SecretKeySpec("9b23e065d8a01768276c21446fc2f67f".getBytes(), "HmacSHA256"));
				} catch (InvalidKeyException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					
					out.println(e);
					out.close();
					return;	
				}
				
				// compute the hash
				byte[] mac_data = sha256_HMAC.doFinal(encoded_tokens[1].getBytes());

				// compare hash strings
				if((new String(mac_data)).equals(encoded_tokens[0]))
				{
					out.println("Bad Signed JSON signature!");
					out.close();
					return;	
				}
			}
				
			// if we get to this point, we know that we have a valid signed request
				
			if("canvas_landing".equals(action))
			{	
				// if the user_id is null, the user is not signed in.  Send the user to the fb auth page.
				if(null == req.user_id)
				{
					out.println("<html><head><meta http-equiv=\"Refresh\" content=\"0; url=/WandaSocial/faces/pages/fb/auth/fbAuthenticateFacebook.xhtml\"</head></html>");
				}
				
				// we are signed in
				else
				{
					// create a new WandaSocialDbAdapter and pass it to the session bean
					WandaSocialDbAdapter social = new WandaSocialDbAdapter(req.user_id);

					// make sure we have the most recent token
					social.setOauth_token(req.oauth_token);
					social.setToken_expires(req.token_expires);
					
					sessionBean.setSocialDbAdapter(social);
					
					// create a new FacebookAdapter and pass it to the session bean
					FacebookUserAdapter facebookAdapter = new FacebookUserAdapter(req.user_id, req.oauth_token, req.token_expires);
					
					sessionBean.setFacebookAdapter(facebookAdapter);

					if(social.isUserProvisioned())
					{
						WandaSubjectDbAdapter subject = new WandaSubjectDbAdapter(social.getWanda_subject_id());
						sessionBean.setSubjectDbAdapter(subject);
						// if already provisioned, send the user to the app home
						out.println("<html><head><meta http-equiv=\"Refresh\" content=\"0; url=/WandaSocial/faces/pages/fb/dashHome.xhtml\"</head></html>");
					}
					
					else
					{
						// not provisioned - continue with the next step in the provisioning
						out.println("<html><head><meta http-equiv=\"Refresh\" content=\"0; url=/WandaSocial/faces/pages/fb/auth/fbAuthenticateStudy.xhtml\"</head></html>");
					}
				}
			}
			
			// sent when the user removes the app from FB.  Need to delete the user from the social db.
			else if("deauthorize_user".equals(action))
			{
				if(null != req.user_id)
				{
					WandaSocialDbAdapter social = sessionBean.getSocialDbAdapter();
					
					if(null == social)
					{
						social = new WandaSocialDbAdapter(req.user_id);
					}
					
					social.deleteUser();
					
					sessionBean.setSocialDbAdapter(null);
					
					// delete complete - send the user to the fb app home
					out.println("<html><head><meta http-equiv=\"Refresh\" content=\"0; url=http://apps.facebook.com/wanda_social\"</head></html>");
				}
			}
			
			// todo: erase
			else if("test".equals(action))
			{
				WandaSocialDbAdapter social = sessionBean.getSocialDbAdapter();
				
				if(null == social)
				{
					social = new WandaSocialDbAdapter(req.user_id);
				}
				
				social.deleteUser();
				
				sessionBean.setSocialDbAdapter(null);
				
				// delete complete - send the user to the fb app home
				out.println("<html><head><meta http-equiv=\"Refresh\" content=\"0; url=http://apps.facebook.com/wanda_social\"</head></html>");
			}
			
			else
			{
				// undefined action - send the user to the fb app home
				out.println("<html><head><meta http-equiv=\"Refresh\" content=\"0; url=http://apps.facebook.com/wanda_social\"</head></html>");
		
			}
		}
		
		out.close();
	}
	
	private class SignedRequest
	{
		public String algorithm, user_id, oauth_token, token_expires;
		public SignedRequest() {};
	}

}
